//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#include <ddk.h>
#include <bsp.h>
#include "slip.h"

#define COM_DBG_FLAG   0

ECode SLIP::Read(
    /* [in] */ UInt64 u64Offset,
    /* [in] */ UInt32 uNumberOfBytesToRead,
    /* [out] */ EzByteBuf ebbData,
    /* [out] */ IEvent * * ppCompletionEvent)
{
    if ( ebbData.IsNull() ||
         0 >= ebbData.GetCapacity() ||
         uNumberOfBytesToRead > (UInt32)ebbData.GetCapacity())
        return E_INVALID_ARGUMENT;

    if (uNumberOfBytesToRead >SLIP_PACK_MAX_SIZE){
        uNumberOfBytesToRead = SLIP_PACK_MAX_SIZE;
        kprintf("exceed the limit of slip packet,only read "\
            "%d bytes\n", uNumberOfBytesToRead);
        }

    UInt32 uNumberOfRead = (UInt32)slip_recv_packet(
                  (UInt8 *)(char *)ebbData, uNumberOfBytesToRead);
#if COM_DBG_FLAG
{
    UInt32 i = 0;
    char * pBuf = (char *)ebbData;
    kprintf("Receive slip packet, Size %d\n", uNumberOfRead);

    for (i=0; i<uNumberOfRead; i++) {
        if ((i % 16 == 0) && (i != 0))
            kprintf("\n");
        kprintf("%02x ", pBuf[i]);
    }
    kprintf("\n");
}
#endif
    ebbData.SetUsed(uNumberOfRead);
    if (uNumberOfBytesToRead == uNumberOfRead)
        return NOERROR;
    else
        return S_FALSE;
}

ECode SLIP::Write(
    /* [in] */ UInt64 u64Offset,
    /* [in] */ EzByteBuf ebbData,
    /* [out] */ UInt32 * puNumberOfBytesWritten,
    /* [out] */ IEvent * * ppCompletionEvent)
{
    if ( ebbData.IsNull() ||
         0 >= ebbData.GetUsed() ||
         NULL == puNumberOfBytesWritten)
        return E_INVALID_ARGUMENT;

#if COM_DBG_FLAG
{
    UInt32 i = 0;
    UInt32 Size = ebbData.GetUsed();
    char * pBuf = (char *)ebbData;
    kprintf("Send slip packet, Size %d\n", Size);

    for (i =0; i< Size; i++) {
        if ((i % 16 == 0) && (i != 0))
            kprintf("\n");
        kprintf("%02x ", pBuf[i]);
    }
    kprintf("\n");
}
#endif
    if (!slip_send_packet((UInt8 *)(char *)ebbData, (UInt32)ebbData.GetUsed())) {
        * puNumberOfBytesWritten = (UInt32)ebbData.GetUsed();
        return NOERROR;
    }
    else
        return E_FAIL;
}

ECode SLIP::Control(
    /* [in] */ Int32 nControlCode,
    /* [in] */ EzByteBuf ebbInData,
    /* [out] */ EzByteBuf * pOutData,
    /* [out] */ IEvent * * ppCompletionEvent)
{
    return E_NOT_IMPLEMENTED;
}

void SLIP::Dispose()
{
    delete this;
}

EXTERN_C void PrintComPort(const char *szMessage);
ECode SLIP::Initialize(void)
{
    kprintf("initialize slip\n");
    return NOERROR;
}

/////////////////SLIP PROTOCAL//////////////////////////////////////
inline void SLIP::send_char(unsigned char c)
{
    BspWriteComPort(ComPort1, c);
}
//Return 0 when SUCCESS
inline int SLIP::recv_char(unsigned char *p)
{
    if (BspReadComPort(ComPort1, p))
        return SLIP_RECV_SUCCESS;
    else
        return SLIP_RECV_ERROR;
}

/* slip_send_packet: sends a packet of length "len", starting at
 *                   location "p".
 */
int SLIP::slip_send_packet(UInt8 *p, Int32 len)
{
    /* send an initial END character to flush out any data that may
     * have accumulated in the receiver due to line noise
     */
    send_char(SLIP_END);
    while(len--) {
        switch(*p) {
            /* if it's the same code as an END character, we send a
             * special two character code so as not to make the
             * receiver think we sent an END
             */
            case SLIP_END:
                send_char(SLIP_ESC);
                send_char(SLIP_ESC_END);
                break;

            /* if it's the same code as an ESC character,
             * we send a special two character code so as not
             * to make the receiver think we sent an ESC
             */
            case SLIP_ESC:
                send_char(SLIP_ESC);
                send_char(SLIP_ESC_ESC);
                break;

            /* otherwise, we just send the character
             */
            default:
                send_char(*p);
        }

        p++;
    }

    /* tell the receiver that we're done sending the packet
     */
    send_char(SLIP_END);
    return 0;
}

/* slip_recv_packet: receives a packet into the buffer located at "p".
 *                   If more than len bytes are received, the packet will
 *                   be truncated.
 *                   Returns the number of bytes stored in the buffer.
 */
int SLIP::slip_recv_packet(UInt8 *p, Int32 len)
{
    unsigned char c;
    int received = 0;
	int bRecvFlag = 0;

    while(1) {
        if (recv_char(&c) == SLIP_RECV_SUCCESS) {
#if  COM_DBG_FLAG
            kprintf("slip_recv_packet() receive a char : %02x\n", c);
#endif
            switch(c) {

                /* if it's an END character then we're done with
                 * the packet
                 */
                case SLIP_END:
                    if (received)
                        return received;
                    else{
						bRecvFlag = 1;
                        break;
					}

                /* if it's the same code as an ESC character, wait
                 * and get another character and then figure out
                 * what to store in the packet based on that.
                 */
                case SLIP_ESC:
                    if (recv_char(&c) == SLIP_RECV_SUCCESS) {
#if  COM_DBG_FLAG
                        kprintf("in slip_recv_packet() get a char : %02x\n", c);
#endif
                        /* if "c" is not one of these two, then we
                         * have a protocol violation.  The best bet
                         * seems to be to leave the byte alone and
                         * just stuff it into the packet
                         */
                        switch(c) {
                            case SLIP_ESC_END:
                                c = (unsigned char)SLIP_END;
                                break;
                            case SLIP_ESC_ESC:
                                c = (unsigned char)SLIP_ESC;
                                break;
                        }
                    }
                    else
                        return 0;
                default:
                    if(received < len && bRecvFlag)
                        p[received++] = c;
            }
        }
        else {
#if  COM_DBG_FLAG
            kprintf("slip_recv_packet() receive error\n");
#endif
            return 0;
            }
    }
}
/////////////////SLIP PROTOCAL END//////////////////////////////////

EXTERN IDeviceDriver * CDECL CreateSLIP(uint_t uDeviceNo, void *pvParameter)
{
    SLIP *pSLIP = new SLIP;
    if (NULL == pSLIP) return NULL;

    if (FAILED(pSLIP->Initialize())) {
        delete pSLIP;
        return NULL;
    }

    pSLIP->AddRef();

    return pSLIP;
}
